package battleship;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

/**
 *
 * @author kamil
 */
public class ComputerPlayer extends Player {

	ComputerStrategy strategy = ComputerStrategy.Medium;
	private boolean hitNotSunk;
	private Ship actualShip;
	private ArrayList<Coords> shipHits;
	private int area;
	private int maxShip;
	ArrayList<Coords> diagonalList;

	ComputerPlayer(Rules r) {
		rules = r;

		shipHits = new ArrayList<Coords>();
		diagonalList = new ArrayList<Coords>();

		hitNotSunk = false;
		actualShip = null;

		area = 0;
		maxShip = 0;
	}

	Coords fire(Grid fireGrid) {
		Coords c;

		if (hitNotSunk) {
			c = shipSearching(fireGrid);
			if (c != null) {
				return c;
			}

			hitNotSunk = false;
			actualShip = null;
			shipHits.clear();

			assert false : "should never happened";
		}

		Random rnd = new Random();
		int x, y;

		if (strategy == ComputerStrategy.Medium) {
			if (maxShip <= 0) {
				for (Ship s : rules.player1.getShips()) {
					if ((s.shipPoints.size() > maxShip) && !s.isSunk()) {
						maxShip = s.shipPoints.size();
					}
				}

				if ((maxShip > 1) && rules.curvedShipsAllowed)
					maxShip /= 2;

				maxShip *= 2;
			}

			if (diagonalList.isEmpty()) {
				maxShip /= 2;

				if (maxShip <= 1) {
					strategy = ComputerStrategy.Easy;
					return getEasyCoords(fireGrid);
				}

				int diagonal = 0;

				while (diagonal < (1 + rules.xSize + rules.ySize) / maxShip) {
					for (int i = 0; i <= maxShip * diagonal; i++) {
						if ((i < rules.ySize) && (maxShip * diagonal - i < rules.xSize)
								&& (fireGrid.fields[i][maxShip * diagonal - i].fieldType
								== FieldType.NORMAL)) {
							diagonalList.add(new Coords(maxShip * diagonal - i, i, null));
						}
					}
					diagonal++;
				}
			}

			return diagonalList.remove(rnd.nextInt(diagonalList.size()));
		}

		if (strategy == ComputerStrategy.Easy)
			return getEasyCoords(fireGrid);

		if (strategy == ComputerStrategy.ExtraHard) {
			while (rnd.nextInt(3) != 0) {
				x = rnd.nextInt(rules.xSize);
				y = rnd.nextInt(rules.ySize);

				if (fireGrid.fields[y][x].fieldType == FieldType.NORMAL) {
					return new Coords(x, y, null);
				}
			}

			return cheatAndGetNextPoint();
		}

		return null;
	}

	Coords getEasyCoords(Grid fireGrid) {
		Random rnd = new Random();
		int x, y;

		if (area == 0) {
			area = rules.xSize * rules.ySize;
		}

		for (int counter = 0; counter < area; counter++) {
			x = rnd.nextInt(rules.xSize);
			y = rnd.nextInt(rules.ySize);

			if (fireGrid.fields[y][x].fieldType == FieldType.NORMAL) {
				return new Coords(x, y, null);
			}
		}

		return cheatAndGetNextPoint();
	}

	Coords shipSearching(Grid fireGrid) {
		for (int i = shipHits.size() - 1; i >= 0; i--) {
			Coords h = shipHits.get(i);
			Collection<Field> neighbours =
					fireGrid.getNeighbours(fireGrid.fields[h.y][h.x]);

			if (rules.curvedShipsAllowed || (shipHits.size() == 1)) {
				for (Field f : neighbours) {
					if (f.fieldType == FieldType.NORMAL) {
						return new Coords(f.col, f.row, null);
					}
				}
			} else {
				// looking for direction
				Field tmp = null;
				for (Field f : neighbours) {
					if (f.fieldType == FieldType.HIT) {
						tmp = f;
					}
				}
				assert (tmp != null) : "there should be hit neighbour";

				if (tmp.row == h.y) {
					int col = 2 * h.x - tmp.col;
					if ((col >= 0) && (col < rules.xSize)
							&& (fireGrid.fields[h.y][col].fieldType == FieldType.NORMAL)) {
						return new Coords(col, h.y, null);
					}
				} else if (tmp.col == h.x) {
					int row = 2 * h.y - tmp.row;
					if ((row >= 0) && (row < rules.ySize)
							&& (fireGrid.fields[row][h.x].fieldType == FieldType.NORMAL)) {
						return new Coords(h.x, row, null);
					}
				} else assert false : "can't go diagonally";
			}
		}

		return null;
	}

	void shipHitInfo(Coords c) {
		if (c.owner.type == ShipType.Destroyer) {
			hitNotSunk = false;
			actualShip = null;
			shipHits.clear();

			assert false : "can't hit and not sank 1-field ship";
			return;
		}

		if (hitNotSunk && (c.owner != actualShip)) {
			assert false : "hit ship that is not searching by us";
			return;
		}

		actualShip = c.owner;
		hitNotSunk = true;
		shipHits.add(c);
	}

	void shipSunkInfo(Coords c) {
		hitNotSunk = false;
		maxShip = 0;
		shipHits.clear();

		 assert (c.owner == actualShip) : "ship sank which we don't search for";

		actualShip = null;
	}

	Coords cheatAndGetNextPoint() {
		for (Ship s : rules.player1.getShips()) {
			for (Coords c : s.shipPoints) {
				if (!c.isHit()) {
					return c;
				}
			}
		}

		return null;
	}
}
